"""

"""

import logging
from abc import ABC, abstractmethod
from typing import Any

from core.object import (
    TickData,
    OrderData,
    TradeData,
    PositionData,
    AccountData,
    ContractData,
    OrderRequest,
    CancelRequest,
    SubscribeRequest,
    SettlementInfo, InvestorInfo, OrderDataError,
)
from event import Event, EventEngine
from event.event_def import *


class BaseGateway(ABC):
    """
    Abstract gateway class for creating gateways connection 
    to different trading systems.

    # How to implement a gateway:

    ---
    ## Basics
    A gateway should satisfies:
    * this class should be thread-safe:
        * all methods should be thread-safe
        * no mutable shared properties between objects.
    * all methods should be non-blocked
    * satisfies all requirements written in docstring for every method and callbacks.
    * automatically reconnect if connection lost.

    ---
    ## methods must implements:
    all @abstractmethod

    ---
    ## callbacks must response manually:
    * on_tick
    * on_trade
    * on_order
    * on_position
    * on_account
    * on_contract

    All the XxxData passed to callback should be constant, which means that
        the object should not be modified after passing to on_xxxx.
    So if you use a cache to store reference of data, use copy.copy to create a new object
    before passing that data into on_xxxx



    """

    # Fields required in setting dict for connect function.
    default_setting = {}

    def __init__(self, event_engine: EventEngine, gateway_name: str):
        """"""
        self.event_engine = event_engine
        self.gateway_name = gateway_name

        self.logger = logging.getLogger('gateway')
        self.last_err = ''

    def on_event(self, type: str, data: Any = None):
        """
        General event push.
        """
        event = Event(type, data)
        self.event_engine.put(event)

    def on_tick(self, tick: TickData):
        """
        Tick event push.
        Tick event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_TICK, tick)
        # self.on_event(EVENT_TICK + tick.vt_symbol, tick)

    def on_trade(self, trade: TradeData):
        """
        Trade event push.
        Trade event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_TRADE, trade)
        # self.on_event(EVENT_TRADE + trade.vt_symbol, trade)

    def on_order(self, order: OrderData):
        """
        Order event push.
        Order event of a specific vt_orderid is also pushed.
        """
        self.on_event(EVENT_ORDER, order)
        # self.on_event(EVENT_ORDER + order.vt_orderid, order)
    def on_order_error(self, order: OrderDataError):
        """
        Order event push.
        Order event of a specific vt_orderid is also pushed.
        """
        self.on_event(EVENT_ORDER_ERROR, order)
        # self.on_event(EVENT_ORDER + order.vt_orderid, order)

    def on_cancel_order_rejected(self, data):
        self.on_event(EVENT_CANCEL_ORDER_REJECTED, data)

    def on_position(self, position: PositionData):
        """
        Position event push.
        Position event of a specific vt_symbol is also pushed.
        """
        self.on_event(EVENT_POSITION, position)
        # self.on_event(EVENT_POSITION + position.vt_symbol, position)

    def on_all_positions(self):
        # No data need
        self.on_event(EVENT_ALL_POSITIONS)

    def on_account(self, account: AccountData):
        """
        Account event push.
        Account event of a specific vt_accountid is also pushed.
        """
        self.on_event(EVENT_ACCOUNT, account)
        # self.on_event(EVENT_ACCOUNT + account.vt_accountid, account)

    def on_contract(self, contract: ContractData):
        """
        Contract event push.
        """
        self.on_event(EVENT_CONTRACT, contract)

    def on_settlement_info(self, settlementInfo: SettlementInfo):
        """        查询结算单        """
        self.on_event(EVENT_SETTLEMENT_INFO, settlementInfo)

    def on_investor(self, investorInfo: InvestorInfo):
        """查询投资者信息        """
        self.on_event(EVENT_Investor, investorInfo)

    def write_log(self, msg: str):
        self.logger.info(msg)

    @abstractmethod
    def connect(self, setting: dict):
        """
        Start gateway connection.

        to implement this method, you must:
        * connect to server if necessary
        * log connected if all necessary connection is established
        * do the following query and response corresponding on_xxxx and write_log
            * contracts : on_contract
            * account asset : on_account
            * account holding: on_position
            * orders of account: on_order
            * trades of account: on_trade
        * if any of query above is failed,  write log.

        future plan:
        response callback/change status instead of write_log

        """
        pass

    @abstractmethod
    def change_password(self, old_password, new_password):
        pass

    @abstractmethod
    def close(self):
        """
        Close gateway connection.
        """
        pass

    @abstractmethod
    def subscribe(self, req: SubscribeRequest):
        """
        Subscribe tick data update.
        """
        pass

    @abstractmethod
    def unsubscribe(self, req: SubscribeRequest):
        """
        Subscribe tick data update.
        """
        pass

    @abstractmethod
    def send_order(self, req: OrderRequest) -> str:
        """
        Send a new order to server.

        implementation should finish the tasks blow:
        * create an OrderData from req using OrderRequest.create_order_data
        * assign a unique(gateway instance scope) id to OrderData.orderid
        * send request to server
            * if request is sent, OrderData.status should be set to Status.SUBMITTING
            * if request is failed to sent, OrderData.status should be set to Status.REJECTED
        * response on_order:
        * return OrderData.vt_orderid

        :return str vt_orderid for created OrderData
        """
        pass

    @abstractmethod
    def cancel_order(self, req: CancelRequest):
        """
        Cancel an existing order.
        implementation should finish the tasks blow:
        * send request to server


        """
        pass

    @abstractmethod
    def query_account(self):
        """
        Query account balance.

        """
        pass

    @abstractmethod
    def query_position(self):
        """
        Query holding positions.
        """
        pass

    @abstractmethod
    def query_order(self):
        """
        Query orders.
        """
        pass

    @abstractmethod
    def is_connected(self):
        pass

    @abstractmethod
    def is_ready(self):
        pass

    @abstractmethod
    def init_query(self):
        pass

    # @abstractmethod
    def query_settlement_info(self, trading_day):
        """请求查询投资者结算结果  trading_day 20180101"""
        pass

    def query_exchange_margin_rate_adjust(self, symbol):
        """请求查询交易所调整保证金率"""
        pass

    def query_investor(self):
        """请求查询投资者"""
        pass
